package cn.shanguoyu.base.springboot.starter.idempotent.aop;

import cn.shanguoyu.base.springboot.starter.idempotent.core.factory.IdempotentExecuteHandlerFactory;
import cn.shanguoyu.base.springboot.starter.idempotent.core.strategy.IdempotentContext;
import cn.shanguoyu.base.springboot.starter.idempotent.core.strategy.IdempotentExecuteHandler;
import cn.shanguoyu.base.springboot.starter.idempotent.core.strategy.impl.spel.RepeatConsumptionException;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;

import java.lang.reflect.Method;

/**
 * @description:幂等注解AOP拦截器
 * @author：sgy
 * @date: 2023-06-03
 */
@Aspect
public class IdempotentAspect {
    /**
     * 增强方法标记 {@link Idempotent} 注解逻辑
     */
    @Around("@annotation(cn.shanguoyu.base.springboot.starter.idempotent.aop.Idempotent)")
    public Object idempotentHandler(ProceedingJoinPoint joinPoint) throws Throwable {
        // 根据连接点获取方法上的幂等注解
        Idempotent idempotent = getIdempotent(joinPoint);
        // 根据幂等类型和场景去工厂获取对应的幂等对象
        IdempotentExecuteHandler instance = IdempotentExecuteHandlerFactory.getInstance(idempotent.scene(), idempotent.type());
        // 执行幂等逻辑
        try {
            instance.execute(joinPoint, idempotent);
            return joinPoint.proceed();
        } catch (RepeatConsumptionException ex) {
            if (!ex.getError()) {
                return null;
            }
            throw ex;
        } finally {
            instance.postProcessing();
            IdempotentContext.clean();
        }
    }


    public static Idempotent getIdempotent(ProceedingJoinPoint joinPoint) throws NoSuchMethodException {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method targetMethod = joinPoint.getTarget().getClass().getDeclaredMethod(methodSignature.getName(), methodSignature.getMethod().getParameterTypes());
        return targetMethod.getAnnotation(Idempotent.class);
    }

}
